/*                                                                           *\
**        _____ ____  _____   _____    __                                    **
**       / ___// __ \/  _/ | / /   |  / /   HDL Core                         **
**       \__ \/ /_/ // //  |/ / /| | / /    (c) Dolu, All rights reserved    **
**      ___/ / ____// // /|  / ___ |/ /___                                   **
**     /____/_/   /___/_/ |_/_/  |_/_____/                                   **
**                                                                           **
**      This library is free software; you can redistribute it and/or        **
**    modify it under the terms of the GNU Lesser General Public             **
**    License as published by the Free Software Foundation; either           **
**    version 3.0 of the License, or (at your option) any later version.     **
**                                                                           **
**      This library is distributed in the hope that it will be useful,      **
**    but WITHOUT ANY WARRANTY; without even the implied warranty of         **
**    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU      **
**    Lesser General Public License for more details.                        **
**                                                                           **
**      You should have received a copy of the GNU Lesser General Public     **
**    License along with this library.                                       **
\*                                                                           */
package spinal.core


/**
  * Ram 1w 1ra
  */
class Ram_1w_1ra(
  val wordWidth      : Int,
  val wordCount      : Int,
  val technology     : MemTechnologyKind = auto,
  val readUnderWrite : ReadUnderWritePolicy = dontCare,

  val wrAddressWidth : Int,
  val wrDataWidth    : Int,
  val wrMaskWidth    : Int = 1,
  val wrMaskEnable   : Boolean = false,

  val rdAddressWidth : Int,
  val rdDataWidth    : Int
) extends BlackBox {

  if (readUnderWrite == readFirst) SpinalError("readFirst mode for asynchronous read is not allowed")

  addGenerics(
    "wordCount"      -> Ram_1w_1ra.this.wordCount,
    "wordWidth"      -> Ram_1w_1ra.this.wordWidth,
    "technology"     -> Ram_1w_1ra.this.technology.technologyKind,
    "readUnderWrite" -> Ram_1w_1ra.this.readUnderWrite.readUnderWriteString,
    "wrAddressWidth" -> Ram_1w_1ra.this.wrAddressWidth,
    "wrDataWidth"    -> Ram_1w_1ra.this.wrDataWidth,
    "wrMaskWidth"    -> Ram_1w_1ra.this.wrMaskWidth,
    "wrMaskEnable"   -> Ram_1w_1ra.this.wrMaskEnable,
    "rdAddressWidth" -> Ram_1w_1ra.this.rdAddressWidth,
    "rdDataWidth"    -> Ram_1w_1ra.this.rdDataWidth
  )

  val io = new Bundle {
    val clk = in.Bool()

    val wr = new Bundle {
      val en   = in.Bool()
      val mask = in Bits(wrMaskWidth bits)
      val addr = in UInt(wrAddressWidth bit)
      val data = in Bits(wrDataWidth bit)
    }

    val rd = new Bundle {
      val addr =  in UInt(rdAddressWidth bit)
      val data = out Bits(rdDataWidth bit)
    }
  }

  mapCurrentClockDomain(io.clk)
  noIoPrefix()
}


/**
  * Ram 1w 1rs
  */
class Ram_1w_1rs(
  val wordWidth      : Int,
  val wordCount      : Int,
  val readUnderWrite : ReadUnderWritePolicy = dontCare,
  val technology     : MemTechnologyKind = auto,

  val wrClock        : ClockDomain,
  val wrAddressWidth : Int,
  val wrDataWidth    : Int,
  val wrMaskWidth    : Int = 1,
  val wrMaskEnable   : Boolean = false,

  val rdClock        : ClockDomain,
  val rdAddressWidth : Int,
  val rdDataWidth    : Int
) extends BlackBox {

  addGenerics(
    "wordCount"      -> Ram_1w_1rs.this.wordCount,
    "wordWidth"      -> Ram_1w_1rs.this.wordWidth,
    "clockCrossing"  -> (wrClock != rdClock),
    "technology"     -> Ram_1w_1rs.this.technology.technologyKind,
    "readUnderWrite" -> Ram_1w_1rs.this.readUnderWrite.readUnderWriteString,
    "wrAddressWidth" -> Ram_1w_1rs.this.wrAddressWidth,
    "wrDataWidth"    -> Ram_1w_1rs.this.wrDataWidth,
    "wrMaskWidth"    -> Ram_1w_1rs.this.wrMaskWidth,
    "wrMaskEnable"   -> Ram_1w_1rs.this.wrMaskEnable,
    "rdAddressWidth" -> Ram_1w_1rs.this.rdAddressWidth,
    "rdDataWidth"    -> Ram_1w_1rs.this.rdDataWidth
  )


  val io = new Bundle {
    val wr = new Bundle {
      val clk  = in Bool()
      val en   = in Bool()
      val mask = in Bits(wrMaskWidth bits)
      val addr = in UInt(wrAddressWidth bit)
      val data = in Bits(wrDataWidth bit)
    }

    val rd = new Bundle {
      val clk  = in Bool()
      val en   = in Bool()
      val addr = in  UInt(rdAddressWidth bit)
      val data = out Bits(rdDataWidth bit)
    }
  }

  mapClockDomain(wrClock,io.wr.clk)
  mapClockDomain(rdClock,io.rd.clk)
  noIoPrefix()
}


/**
  * Ram 2c 1w 1rs
  */
class Ram_2c_1w_1rs(
  val wordWidth      : Int,
  val wordCount      : Int,
  val wrClock        : ClockDomain,
  val rdClock        : ClockDomain,
  val maskWidth      : Int = 1,
  val useMask        : Boolean = false,
  val readUnderWrite : ReadUnderWritePolicy = dontCare,
  val tech           : MemTechnologyKind = auto
) extends BlackBox {

  addGenerics(
    "wordCount"      -> Ram_2c_1w_1rs.this.wordCount,
    "wordWidth"      -> Ram_2c_1w_1rs.this.wordWidth,
    "maskWidth"      -> Ram_2c_1w_1rs.this.maskWidth,
    "readUnderWrite" -> Ram_2c_1w_1rs.this.readUnderWrite.readUnderWriteString,
    "tech"           -> Ram_2c_1w_1rs.this.tech.technologyKind,
    "useReadEnable"  -> true,
    "useMask"        -> Ram_2c_1w_1rs.this.useMask
  )

  val io = new Bundle {
    val wr = new Bundle {
      val clk  = in Bool()
      val en   = in Bool()
      val mask = in Bits(maskWidth bits)
      val addr = in UInt(log2Up(wordCount) bit)
      val data = in Bits(wordWidth bit)
    }

    val rd = new Bundle {
      val clk  =  in Bool()
      val en   =  in Bool()
      val addr =  in UInt(log2Up(wordCount) bit)
      val data = out Bits(wordWidth bit)
    }
  }

  mapClockDomain(wrClock,io.wr.clk)
  mapClockDomain(rdClock,io.rd.clk)
  noIoPrefix()
}


/**
  * Ram 1wors
  */
class Ram_1wors(val wordWidth: Int,
                val wordCount: Int,
                val readUnderWrite: ReadUnderWritePolicy = dontCare) extends BlackBox {

  addGenerics(
    "wordCount"      -> Ram_1wors.this.wordCount,
    "wordWidth"      -> Ram_1wors.this.wordWidth,
    "readUnderWrite" -> Ram_1wors.this.readUnderWrite.readUnderWriteString,
    "useReadEnable"  -> true
  )

  val io = new Bundle {
    val clk = in Bool()

    val addr = in UInt(log2Up(wordCount) bit)

    val wr = new Bundle {
      val en   = in Bool()
      val data = in Bits(wordWidth bit)
    }

    val rd = new Bundle {
      val en   =  in Bool()
      val data = out Bits(wordWidth bit)
    }
  }

  mapCurrentClockDomain(io.clk)
  noIoPrefix()

  //Following is not obligatory, just to describe blackbox logic
  val mem = Mem(io.wr.data, wordCount)
  when(io.wr.en) {
    mem.write(io.addr, io.wr.data)
  }
  io.rd.data := mem.readSync(io.addr, io.rd.en)
}


/**
  * Ram 1wrs
  */
class Ram_1wrs(
  val wordWidth      : Int,
  val wordCount      : Int,
  val technology     : MemTechnologyKind,
  val readUnderWrite : ReadUnderWritePolicy = dontCare,
  val duringWrite    : DuringWritePolicy = dontCare,
  val maskWidth      : Int,
  val maskEnable     : Boolean
) extends BlackBox {

  addGenerics(
    "wordCount"      -> Ram_1wrs.this.wordCount,
    "wordWidth"      -> Ram_1wrs.this.wordWidth,
    "readUnderWrite" -> Ram_1wrs.this.readUnderWrite.readUnderWriteString,
    "duringWrite"    -> Ram_1wrs.this.duringWrite.duringWriteString,
    "technology"     -> Ram_1wrs.this.technology.technologyKind,
    "maskWidth"      -> Ram_1wrs.this.maskWidth,
    "maskEnable"     -> Ram_1wrs.this.maskEnable
  )

  val io = new Bundle {
    val clk    =  in Bool()
    val en     =  in Bool()
    val wr     =  in Bool()
    val addr   =  in UInt(log2Up(wordCount) bit)
    val mask   =  in Bits(maskWidth bits)
    val wrData =  in Bits(wordWidth bit)
    val rdData = out Bits(wordWidth bit)
  }

  mapCurrentClockDomain(io.clk)
  noIoPrefix()
}


/**
  * Ram 2wrs
  */
class Ram_2wrs(
  val wordWidth            : Int,
  val wordCount            : Int,
  val technology           : MemTechnologyKind,

  val portA_readUnderWrite : ReadUnderWritePolicy = dontCare,
  val portA_duringWrite    : DuringWritePolicy = dontCare,
  val portA_clock          : ClockDomain,
  val portA_addressWidth   : Int,
  val portA_dataWidth      : Int,
  val portA_maskWidth      : Int,
  val portA_maskEnable     : Boolean,

  val portB_readUnderWrite : ReadUnderWritePolicy = dontCare,
  val portB_duringWrite    : DuringWritePolicy = dontCare,
  val portB_clock          : ClockDomain,
  val portB_addressWidth   : Int,
  val portB_dataWidth      : Int,
  val portB_maskWidth      : Int,
  val portB_maskEnable     : Boolean
) extends BlackBox {

  addGenerics(
    "wordCount"            -> Ram_2wrs.this.wordCount,
    "wordWidth"            -> Ram_2wrs.this.wordWidth,
    "clockCrossing"        -> (portA_clock != portB_clock),
    "technology"           -> Ram_2wrs.this.technology.technologyKind,
    "portA_readUnderWrite" -> Ram_2wrs.this.portA_readUnderWrite.readUnderWriteString,
    "portA_duringWrite"    -> Ram_2wrs.this.portA_duringWrite.duringWriteString,
    "portA_addressWidth"   -> Ram_2wrs.this.portA_addressWidth,
    "portA_dataWidth"      -> Ram_2wrs.this.portA_dataWidth,
    "portA_maskWidth"      -> Ram_2wrs.this.portA_maskWidth,
    "portA_maskEnable"     -> Ram_2wrs.this.portA_maskEnable,
    "portB_readUnderWrite" -> Ram_2wrs.this.portB_readUnderWrite.readUnderWriteString,
    "portB_duringWrite"    -> Ram_2wrs.this.portB_duringWrite.duringWriteString,
    "portB_addressWidth"   -> Ram_2wrs.this.portB_addressWidth,
    "portB_dataWidth"      -> Ram_2wrs.this.portB_dataWidth,
    "portB_maskWidth"      -> Ram_2wrs.this.portB_maskWidth,
    "portB_maskEnable"     -> Ram_2wrs.this.portB_maskEnable
  )

  val io = new Bundle {
    val portA = new Bundle {
      val clk    =  in Bool()
      val en     =  in Bool()
      val wr     =  in Bool()
      val mask   =  in Bits(portA_maskWidth bits)
      val addr   =  in UInt(portA_addressWidth bit)
      val wrData =  in Bits(portA_dataWidth bit)
      val rdData = out Bits(portA_dataWidth bit)
    }

    val portB = new Bundle {
      val clk    =  in Bool()
      val en     =  in Bool()
      val wr     =  in Bool()
      val mask   =  in Bits(portB_maskWidth bits)
      val addr   =  in UInt(portB_addressWidth bit)
      val wrData =  in Bits(portB_dataWidth bit)
      val rdData = out Bits(portB_dataWidth bit)
    }
  }

  mapClockDomain(portA_clock,io.portA.clk)
  mapClockDomain(portB_clock,io.portB.clk)
  noIoPrefix()
}